package com.lagou.minicat;

import com.lagou.minicat.classloader.MyClassLoader;
import com.lagou.minicat.connector.Connector;
import com.lagou.minicat.container.Context;
import com.lagou.minicat.container.Engine;
import com.lagou.minicat.container.Host;
import com.lagou.minicat.container.Wrapper;
import com.lagou.minicat.service.Service;
import com.lagou.minicat.servlet.Servlet;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName BootStrap
 * @Description 核心启动类
 * @Author playboy
 * @Date 2021/5/22 10:44 上午
 * @Version 1.0
 **/
public class BootStrap {
	private static Logger log = LoggerFactory.getLogger(BootStrap.class);

	public static void main(String[] args) throws Exception {
		BootStrap bootStrap = new BootStrap();

		//1.先加载server.xml
		List<Service> services = bootStrap.load();

		//2.加载host下的webapps
		bootStrap.loadApps(services);
		//3.开启监听
		bootStrap.start(services);

	}

	/**
	 * 1.先加载server.xml
	 *
	 * @return
	 * @throws DocumentException
	 */
	public List<Service> load() throws DocumentException {
		List<Service> services = new ArrayList<>();
		InputStream in = BootStrap.class.getClassLoader().getResourceAsStream("server.xml");

		SAXReader saxReader = new SAXReader();
		Document read = saxReader.read(in);
		//<Server>
		Element rootElement = read.getRootElement();
		//<Service>
		List<Element> serviceNodes = rootElement.selectNodes("//Service");
		for (Element serviceNode : serviceNodes) {
			Service service = new Service();
			services.add(service);
			//<Connector port="8080"/>
			List<Element> connectorNodes = serviceNode.selectNodes("//Connector");
			for (Element connectorNode : connectorNodes) {
				String port = connectorNode.attributeValue("port");
				Connector connector = new Connector(Integer.valueOf(port));
				service.getConnectors().add(connector);
			}
			//<Engine>
			Node node = serviceNode.selectSingleNode("//Engine");
			Engine engine = new Engine();
			service.setEngine(engine);
			//<Host name="localhost"  appBase="/Users/playboy/dev/code/homework/homework5/code/webapps">
			List<Element> hostNodes = node.selectNodes("//Host");
			for (Element hostNode : hostNodes) {
				String name = hostNode.attributeValue("name");
				String appBase = hostNode.attributeValue("appBase");
				Host host = new Host(name, appBase);
				engine.getHosts().add(host);
			}
		}
		return services;
	}


	/**
	 * 加载webapps下的项目
	 *
	 * @param services
	 */
	private void loadApps(List<Service> services) throws Exception {
		for (Service service : services) {
			List<Host> hosts = service.getEngine().getHosts();

			for (Host host : hosts) {
				service.getMapper().addHost(host.getHostName(), host);
				//获取appBase,然后解析加载
				String appBase = host.getAppBase();
				//：/Users/playboy/dev/code/homework/homework5/code/webapps
				File file = new File(appBase);
				if (file.isDirectory()) {
					//获取contexts
					File[] contexts = file.listFiles();
					for (File contextPath : contexts) {
						//只能解析文件夹
						//：/Users/playboy/dev/code/homework/homework5/code/webapps/demo2
						if (contextPath.isDirectory()) {
							//创建一个context，加载context，就是解析web.xml,然后反射创建serlvet，并注册servlet
							String contextName = "/" + contextPath.getName();
							Context context = new Context(contextName);
							service.getMapper().addContext(contextName, context, host);

							//获取当前context的classpath
							//：/Users/playboy/dev/code/homework/homework5/code/webapps/demo2/server
							File classPath = new File(contextPath.getAbsolutePath() + File.separator + "server");
							ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
							//创建当前context的类加载器
							URLClassLoader urlClassLoader = new MyClassLoader(new URL[]{classPath.toURI().toURL()}, BootStrap.class.getClassLoader());
							context.setClassLoader(urlClassLoader);
							//获取web.xml
							InputStream in = new FileInputStream(new File(contextPath.getAbsolutePath() + File.separator + "web.xml"));
							//解析web.xml
							Map<String, Servlet> servletMapping = loadApp(in, urlClassLoader);
							for (Map.Entry<String, Servlet> entry : servletMapping.entrySet()) {
								String key = entry.getKey();
								Servlet value = entry.getValue();
								Wrapper wrapper = new Wrapper(value);
								service.getMapper().addWrapper(host, context, key, wrapper);
							}
						}
					}
				}
			}
		}
	}


	public Map<String, Servlet> loadApp(InputStream in, URLClassLoader urlClassLoader) throws Exception {
		Map<String, Servlet> servletMapping = new HashMap<>();
		SAXReader saxReader = new SAXReader();
		Document read = saxReader.read(in);
		Element rootElement = read.getRootElement();

		List<Element> servletMappings = rootElement.selectNodes("//servlet-mapping");
		Map<String, String> servletUrl = new HashMap<>();
		for (Element servletNameMapping : servletMappings) {
			Node name = servletNameMapping.selectSingleNode("//servlet-name");
			String servletName = name.getText();
			Node url = servletNameMapping.selectSingleNode("//url-pattern");
			String urlPattern = url.getText();
			servletUrl.put(servletName, urlPattern);
		}

		List<Element> servletClasss = rootElement.selectNodes("//servlet");

		for (Element servletClass : servletClasss) {
			Node name = servletClass.selectSingleNode("//servlet-name");
			String servletName = name.getText();
			Node clazz = servletClass.selectSingleNode("//servlet-class");
			String aClass = clazz.getText();
			Servlet servlet = (Servlet) urlClassLoader.loadClass(aClass).newInstance();
			servletMapping.put(servletUrl.get(servletName), servlet);
		}
		return servletMapping;
	}


	/**
	 * 开启监听
	 *
	 * @param services
	 */
	private void start(List<Service> services) {
		for (Service service : services) {
			List<Connector> connectors = service.getConnectors();
			for (Connector connector : connectors) {
				connector.setService(service);
				connector.start();
			}
		}
	}

}
